package com.snow.component;

import com.snow.exception.CommonRuntimeException;
import com.snow.exception.UnLockException;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;

@Component
public class RedisLock {

    private final StringRedisTemplate stringRedisTemplate;

    public RedisLock(StringRedisTemplate stringRedisTemplate) {
        this.stringRedisTemplate = stringRedisTemplate;
    }

    public boolean lock(String lockKey, String value, long timeout) {
        return Boolean.TRUE.equals(stringRedisTemplate.opsForValue().setIfAbsent(lockKey, value, timeout, TimeUnit.SECONDS));
    }

    public boolean lock(String lockKey) {
        return Boolean.TRUE.equals(stringRedisTemplate.opsForValue().setIfAbsent(lockKey, "", 30 * 60, TimeUnit.SECONDS));
    }

    /**
     *
     */
    public void lock(String key, Runnable handler) throws UnLockException {
        boolean locked = lock(key);
        if (locked) {
            try {
                handler.run();
            } finally {
                unlock(key);
            }
        } else {
            throw new UnLockException();
        }
    }

    public <T> T lock(String key, Supplier<T> handler) throws UnLockException {
        boolean locked = lock(key);
        if (locked) {
            try {
                return handler.get();
            } finally {
                unlock(key);
            }
        } else {
            throw new UnLockException();
        }
    }

    public <T> T lock(String key, Supplier<T> handler, String errorMessage) {
        try {
            return lock(key, handler);
        } catch (UnLockException e) {
            throw new CommonRuntimeException(errorMessage);
        }
    }

    public void unlock(String lockKey) {
        stringRedisTemplate.delete(lockKey);
    }

}
